#include "clib_dpdk_port.h"
#include "inc/clib_log.h"


#define CLIB_NIC_RSS_L3         1
#define CLIB_NIC_RSS_L3l4       2 

#define ALIGN_SIZE              64

static struct rte_eth_conf g_port_conf = {
    .rxmode = {
        .mq_mode = RTE_ETH_MQ_RX_NONE,
    },
    .rx_adv_conf = {
        .rss_conf = {
            .rss_key = NULL,
            .rss_hf = 0,
        },
    },
    .txmode = {
        .mq_mode = RTE_ETH_MQ_TX_NONE,
    },
};

u08_t rss_hash_key_be[CLIB_RSS_HASH_KEY_LENGTH];

static u08_t rss_hash_key[] = {
    0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
    0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
    0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
    0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
    0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
};

static void 
clib_rss_init(void)
{
    rte_convert_rss_key((const u32_t *)rss_hash_key,
                        (u32_t *)rss_hash_key_be, CLIB_RSS_HASH_KEY_LENGTH);
}


/**
 * @brief: 配置以太网端口
 * @param netif 以太网端口结构体
 * @return  返回一个整数，表示函数的成功或失败
 */
static i32_t 
netif_config(clib_netif_t *netif)
{
    i32_t     nb_txd    = 4096;             // 发送描述符数量
    i32_t     nb_rxd    = 4096;             // 接收描述符数量
    i32_t     queues    = netif->queues;    // 队列数量
    u16_t     port_id   = netif->port_id;   // 端口ID
    i32_t     socket_id = netif->socket_id; // 内存分配的NUMA节点
    uint64_t  rss = 0;                      // RSS哈希
    struct rte_mempool *mp;
    struct rte_eth_conf* conf = &netif->eth_conf;// 端口配置
    struct rte_eth_dev_info dev_info;            // 端口信息
    struct rte_eth_link link;
    memset(conf,0,sizeof(struct rte_eth_conf));
    memset(&dev_info,0,sizeof(struct rte_eth_dev_info));
    rte_eth_dev_info_get(port_id, &dev_info);// 获取端口信息

    //设置默认的端口配置
    netif->eth_conf = g_port_conf;

    //如果队列数量超过最大可接受数量，则返回错误
    if(queues > dev_info.max_rx_queues)
    {
        clib_log_error("nic queues(%d) more than max rx queues(%d)\n",queues,dev_info.max_rx_queues);
        return rn_error;
    }
    //如果接收描述符数量超过了最大可接受数量，则将其设置为最大可接受数量
    if (nb_rxd > dev_info.rx_desc_lim.nb_max) {
        nb_rxd = dev_info.rx_desc_lim.nb_max;
    }
    //如果发送描述符数量超过了最大可接受数量，则将其设置为最大可发送数量
    if (nb_txd > dev_info.tx_desc_lim.nb_max) {
        nb_txd = dev_info.tx_desc_lim.nb_max;
    }
    //如果RSS未启用，则将其配置为默认的RTE_ETH_MQ_RX_RSS模式
    if(netif->rss == 0)
    {
        conf->rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;
    }
    //如果RSS已启用，则设置RSS哈希
    if(netif->rss)
    {
        //如果RSS支持L3哈希，则将IP哈希添加到RSS哈希中
        if(netif->rss & CLIB_NIC_RSS_L3) {
            rss |= RTE_ETH_RSS_IP;
        }
        //如果RSS支持L3/L4哈希，则将TCP和UDP哈希添加到RSS哈希中
        if(netif->rss & CLIB_NIC_RSS_L3l4) {
            /** TODO 恢复 **/
            //rss |= (RTE_ETH_RSS_UDP | RTE_ETH_RSS_TCP);

            rss = RTE_ETH_RSS_NONFRAG_IPV4_UDP | RTE_ETH_RSS_NONFRAG_IPV4_TCP |\
                RTE_ETH_RSS_NONFRAG_IPV6_UDP | RTE_ETH_RSS_NONFRAG_IPV6_TCP ;
        }
        
        //定义一个rte_eth_rss_conf结构体rss_conf，设置接收模式为RSS模式，并获取RSS配置结构体
        struct rte_eth_rss_conf *rss_conf;
        conf->rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;
        rss_conf = &conf->rx_adv_conf.rss_conf;
        //设置RSS哈希键和键的长度
        rss_conf->rss_key = rss_hash_key;
        rss_conf->rss_key_len = CLIB_RSS_HASH_KEY_LENGTH,
        //设置RSS哈希
        rss_conf->rss_hf = rss;
    }
    //如果端口支持IPv4校验和卸载，则启用它
    if(dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM)
    {
        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_IPV4_CKSUM;
        g_dev_tx_offload_ipv4_cksum = 1;
    }
    //如果端口支持TCP和UDP校验和卸载，则启用它
    if(dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_TCP_CKSUM \
        && dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_UDP_CKSUM)
    {
        g_dev_tx_offload_tcpudp_cksum = 1;
        conf->txmode.offloads |= (RTE_ETH_TX_OFFLOAD_TCP_CKSUM | RTE_ETH_TX_OFFLOAD_UDP_CKSUM);
    }

    /** 配置速度 **/
    if(dev_info.speed_capa & netif->speeds)
    {
        conf->link_speeds = netif->speeds;
    }

    //使用rte_eth_dev_configure()函数来配置端口
    if(rte_eth_dev_configure(port_id, queues, queues, conf) < 0)
    {
        clib_log_error("dev id(%d) pci(%s) configure fail\n",netif->port_id,netif->pci);
        return rn_error;
    }

    rte_eth_link_get_nowait(port_id, &link);

    clib_log_debug("dpdk link.speed = %d\n",conf->link_speeds);
    // 检查是否成功设置速度
    if (link.link_speed != conf->link_speeds) {
        // 速度设置失败的处理逻辑
        clib_log_debug("dpdk link.speed = %d\n",conf->link_speeds);
    }

    for(i32_t i = 0; i < queues; i++)
    {
        mp = netif->rx_mbuf_pool[i];

        if(rte_eth_rx_queue_setup(port_id, i, nb_rxd, socket_id, NULL, mp) < 0) {
            //配置接收队列失败时，打印错误日志。同样地，如果配置发送队列失败，则打印错误日志
            clib_log_error("dpdk port(%d) setup rx queue(%d) error \n",port_id);
            return rn_error;
        }
        
        if(rte_eth_tx_queue_setup(port_id, i, nb_txd, socket_id, NULL) < 0) {
            clib_log_error("dpdk port(%d) setup tx queue(%d) error \n",port_id);
            return rn_error;
        }
    }

    return rn_ok;
}

/**
 * dpdk port相关初始化.
 *
 * @param netif
 *   ports相关结构
 * @param clib_core
 *   配置文件[core]标签
 * @return
 *  0 成功
 * -1 失败
 */
i32_t
clib_dpdk_netif_init(clib_netif_t *netifs, clib_port_conf_t *conf)
{
    clib_netif_t       *netif;
    clib_port_node_t   *node;
    u16_t               port_id;
    i16_t               socket_id;
    u32_t               nb_pkts;
    u08_t               name[256];

    clib_rss_init();

    for(i32_t i = 0; i < conf->port_cnt; i++) {
        // 获取指定名称的以太网端口ID
        if(rte_eth_dev_get_port_by_name(conf->port[i].pci ,&port_id) != 0) {
            clib_log_error("dpdk pci(%s) get port id error\n", conf->port[i].pci);
            return rn_error;
        }
        socket_id = rte_eth_dev_socket_id(port_id);
        netif = &netifs[port_id];
        strncpy(netif->pci, conf->port[i].pci, sizeof(netif->pci));
        netif->socket_id = socket_id;
        netif->port_id   = port_id;
        netif->queues    = conf->port[i].queues;
        netif->ipv4      = conf->port[i].ipv4;
        netif->ipv4_mask = conf->port[i].ipv4_mask;
        //netif->ip_type = clib_core->ip_type;
        netif->rss       = conf->port[i].rss;
        //netif->promisc = clib_core->promisc;
        netif->speeds   = conf->port[i].speeds;
        rte_eth_macaddr_get(port_id, &netif->local_mac);

        for(i32_t j = 0; j < conf->port[i].queues; j++) {
            node = &netif->node[j];
            nb_pkts = 65535;
            if(conf->port[i].rx_pkts) {
                nb_pkts = conf->port[i].rx_pkts;
            }
            sprintf(name,"rx_mbuf_pool_%d_%d",port_id,j);
            netif->rx_mbuf_pool[j] = rte_pktmbuf_pool_create(name   \
                                    ,nb_pkts                        \
                                    ,ALIGN_SIZE                     \
                                    ,128                            \
                                    ,2048 - (128*2)                 \
                                    ,socket_id);
        }

        if(!netif->rx_mbuf_pool[j])
        {
            clib_log_error("rx mbuf pool malloc port: %d queue: %d error\n",port_id,j);
            return rn_error;
        }

        nb_pkts = 65535; //tnet_core->tx_mbuf > 4096 ? tnet_core->tx_mbuf : 4096;
        
        if(conf->port[i].tx_pkts) {
            nb_pkts = conf->port[i].tx_pkts;
        }
        // 为该节点的TX mbuf池分配内存
        sprintf(name,"tx_mbuf_pool_%d_%d",port_id,j);
        netif->tx_mbuf_pool[j] = rte_pktmbuf_pool_create(name   \
                                ,nb_pkts                        \
                                ,ALIGN_SIZE                     \
                                ,0                              \
                                ,2048 -(128)                    \
                                ,socket_id);
        if(!netif->tx_mbuf_pool[j])
        {
            clib_log_error("tx mbuf pool malloc port: %d queue: %d error\n",port_id,j);
            return rn_error;
        }
        //将节点的信息填充到节点结构体中   
        node->queue         = j;
        node->port_id       = port_id;
        node->socket_id     = socket_id;
        node->rx_mbuf_pool  = netif->rx_mbuf_pool[j];
        node->tx_mbuf_pool  = netif->tx_mbuf_pool[j];
    }

    /** 网卡配置 **/
    for(i32_t i = 0; i < conf->port_cnt; i++) {
        if(netif_config(&netifs[i]) < 0) {
            return rn_error;
        }
    }

    /** 网卡启动 **/
    if(clib_netif_start(netifs, conf->port_cnt) < 0) {
        return rn_error;
    }

    return rn_ok;
}

/**
 * dpdk ports启动.
 *
 * @param netif
 *   ports相关结构
 * @return
 *  0 成功
 * -1 失败
 */
i32_t clib_netif_start(clib_netif_t *netifs,i32_t nb_ports)
{
    clib_netif_t *netif;
    // 遍历所有的以太网端口，调用rte_eth_dev_start()函数来启动端口
    for(i32_t i = 0; i < nb_ports; i++)
    {
        netif = &netifs[i];
        if(rte_eth_dev_start(netif->port_id) !=  0)
        {
            clib_log_error("dpdk port(%s) start error\n",netif->pci);
            return rn_error;
        }
        // 如果端口配置为混杂模式，则启用混杂模式
        if(netif->promisc) {
            rte_eth_promiscuous_enable(netif->port_id);
        }

        //启用所有多播地址
        rte_eth_allmulticast_enable(netif->port_id);

        //重置端口的统计信息
        rte_eth_stats_reset(netif->port_id);
    }

    return rn_ok;
}

/**
 * @brief: 获取端口状态
 * @param netifs 以太网端口状态结构
 * @param nb_ports 以太网端口数量
 * @return  返回一个整数，表示函数的成功或失败
 */
i32_t
clib_netif_get_stats(clib_netif_t *netifs,i32_t nb_ports)
{
    clib_netif_t *netif;
    if(!netifs || !nb_ports) {
        return rn_error;
    }
    for(int32_t i = 0; i < nb_ports; i++) {
        netif = &netifs[i];
        rte_eth_stats_get(netif->port_id, &netif->stats);
    }

    return rn_ok;
}